/* 数据同步规则处理
* */
const DataSyn = {
  enableLog: true,
  triggerName: {
    beforeload: 'beforeload',
    afterload: 'afterload',
    beforesave: 'beforesave',
    reload: 'reload',
    isEmpty: 'isEmpty'
  },
  _split: /[./]/,
  _filterType: [{ type: 'f', des: 'first取第一行' },
    { type: 'all', des: '取所有的行' },
    { type: 'cr', des: 'currentRow当前的行' },
    {
      type: 'eq',
      to: [{ tofield1: 'fromField1' },
        { tofield2: 'fromField2' }],
      des: '目标行过滤条件'
    },
    {
      type: 'on',
      on: {
        tableA: {
          field1: 12,
          field2: 'tableB/field3'
        },
        tableB: {
          field1: 'ABC',
          field4: 'tableC/field15'
        }
      },
      des: '关联过滤'
    }],
  _synRule: { // 所有数值型计算，附加属性toFixed:保留小数据位数
    copy: { fn: '_copy', des: '复制' },
    sum: { fn: '_sum', des: '行相加法' },
    plus: { fn: '_plus', des: '加法' },
    sub: { fn: '_minus', des: '减法' },
    times: { fn: '_times', des: '乖法' },
    divide: { fn: '_divide', des: '除法' },
    join: { fn: '_join', des: '去空+拼接+分隔符,separator:默认为逗号' },
    combine: { fn: '_combine', des: '拼接+无分隔符' },
    avg: { fn: '_avg', des: '平均' },
    count: { fn: '_count', des: '计数' },
    sort: { fn: '_sort', des: '排序,order:(desc?降序)' },
    uniq: { fn: '_uniq', des: '去重' },
    distinct: { fn: '_distinct', des: '去空+去重+排序+拼接,separator:默认为逗号' },
    addDay: { fn: '_addDay', des: '日期添加天数，第二个为天数或是附加属性num:天数' },
    rmb: { fn: '_rmb', des: '金客大写' },
    list: { fn: '_list', des: '列单元集合' },
    select: { fu: '_select', des: '列查询' }
  },
  sortRule: function (rules) {
    var dr = this
    if (dr.enableLog) console.log('sort rule start', rules)
    rules.sort(function (a, b) {
      return a.index - b.index
    })
    if (dr.enableLog) {
      console.log('sort rule end', rules)
    }
  },
  _getTo: function (rule) {
    var t = rule.to.split(this._split)
    var to = { table: t[0], field: t.length > 1 ? t[1] : '', singleField: t.length > 1 }
    return to
  },
  _getPath: function (path) {
    var t = path.split(this._split)
    if (t.length > 1) {
      return { table: t[0], field: t[1], singleField: true }
    } else {
      return { table: t[0], field: '', singleField: false }
    }
  },
  _runRule: function (rule, data, isEmpty) {
    var cr = this.calculates
    var dr = this
    if (dr.enableLog) console.log('runRule：', rule)
    var to = this._getTo(rule)
    let ds
    if (util.typev(data[to.table]) === 'array') {
      ds = data[to.table]
    } else if (data[to.table]) {
      ds = [data[to.table]]
    } else {
      if (dr.enableLog) console.log('not property ' + to.table, rule)
      ds = []
    }
    if (to.singleField) {
      ds.forEach(function (row) {
        if (isEmpty && row[to.field]) {
          return
        }
        var result = dr._getData(data, row, rule, to, rule.from)
        result = cr.toFixed(result, rule)
        row[to.field] = result
      })
    } else {
      if (isEmpty && rule.from.select && (!data[to.table] || data[to.table].length === 0)) {
          var result = dr._select(data, rule, to, rule.from)
          console.log(result)
          data[to.table] = result
      }
    }
  },
  _select: function (data, rule, to, subRule) {
    const select = subRule.select
    const from = subRule.from
    // const swhere = subRule.where // 不支持
    const selectField = []
    select.split(',').forEach(function (item) {
        const items = item.split(' ')
        const tf = items[0].split('.')
        const t = tf.length > 1 ? tf[0] : ''
        const f = tf.length > 1 ? tf[1] : tf[0]
        const asf = items.length > 1 ? items[1] : f
        selectField.push({
          table: t,
          field: f,
          asName: asf
        })
      }
    )
    const dr = this
    let maps = []
    from.split(',').forEach(function (t) {
      const newMaps = []
      const ds = dr._convertToArray(data[t])
      const l = maps.length
      ds.forEach(function (row) {
        const o = {}
        o[t] = row
        const last = maps
        if (l.length > 0) {
          last.forEach(function (item) {
            newMaps.push(Object.assign(o, item))
          })
        } else {
          newMaps.push(o)
        }
      })
      maps = newMaps
    })
    const result = []
    maps.forEach(function (item) {
      const row = {}
      selectField.forEach(function (tf) {
        if (tf.table === '') {
          for (const k in item) {
            const o = item[k]
            // eslint-disable-next-line no-prototype-builtins
            if (o && o.hasOwnProperty(tf.field)) {
              row[tf.asName] = o[tf.field]
            }
          }
        } else {
          const o = item[tf.table]
          row[tf.asName] = o[tf.field]
        }
      })
      result.push(row)
    })
    return result
  },
  _getData: function (data, row, rule, to, subRule) {
    var cr = this.calculates
    var dr = this
    for (var key in this._synRule) {
      if (subRule[key]) {
        if (!dr[this._synRule[key].fn]) {
          console.log('not find function：' + this._synRule[key].fn, subRule)
          continue
        }
        var result = dr[this._synRule[key].fn](data, row, rule, to, subRule)
        result = cr.toFixed(result, subRule)
        return result
      }
    }
  },
  _copy: function (data, row, rule, to, subRule) {
    var dr = this
    if (typeof subRule.copy === 'string') {
      var from = { table: subRule.copy.split(this._split)[0], field: subRule.copy.split(this._split)[1] }
      if (!subRule.filter || subRule.filter === 'f') {
        const ds = this._convertToArray(data[from.table])
        if (ds.length > 0) {
          return ds[0][from.field]
        }
        return ''
      }
      return ''
    }
    var values = []
    values = dr._getValues(data, row, rule, to, subRule, 'sum')
    return values
  },
  _convertToArray: function (obj) {
    if (util.typev(obj) === 'array') {
      return obj
    } else if (obj) {
      return [obj]
    } else {
      return []
    }
  },
  _getRows: function (data, row, subRule, synName) {
    var dr = this
    var rows = []
    var ds = []
    var from = { table: subRule[synName].split(this._split)[0], field: subRule[synName].split(this._split)[1] }
    if (util.typev(data[from.table]) === 'array') {
      ds = data[from.table]
    } else if (data[from.table]) {
      ds = [data[from.table]]
    } else {
      if (dr.enableLog) console.log('not property ' + from.table, subRule)
      ds = []
    }
    if (!subRule.filter || subRule.filter === 'all') {
      rows = ds
    } else if (subRule.filter && subRule.filter.type === 'eq') {
      var filterto = subRule.filter.to
      ds.forEach(function (r) {
        for (var key in filterto) {
          if (r[filterto[key]] !== row[key]) {
            return
          }
        }
        rows.push(r)
      })
    }
    return rows
  },
  _sum: function (data, row, rule, to, subRule) {
    var cr = this.calculates
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'sum')
    var result = 0
    values.forEach(function (r) {
      if (r) {
        result = cr._plus(result, r)
      }
    })
    result = cr.toFixed(result, subRule)
    return result
  },
  _loadHasMaps: function (i, tables, to, row, data, maps) {
    var hasMaps = []
    if (i === tables.length) {
      return maps
    }
    var t = tables[i]

    if (maps.length === 0) {
      if (t === to.table) {
        var m = {}
        m[t] = row
        hasMaps.push(m)
      } else {
        var ds = this._convertToArray(data[tables[i]])
        ds.forEach(function (r) {
          var m = {}
          m[t] = r
          hasMaps.push(m)
        })
      }
    } else {
      maps.forEach(function (item) {
        if (t === to.table) {
          const m = {}
          m[t] = row
          hasMaps.push(Object.assign(item, m))
        } else if (!data[t] || data[t].length === 0) {
          const m = {}
          m[t] = null
          hasMaps.push(Object.assign(item, m))
        } else {
          var ds = this._convertToArray(data[tables[i]])
          ds.forEach(function (r) {
            var m = {}
            m[t] = r
            hasMaps.push(Object.assign(item, m))
          })
        }
      })
    }
    return this._loadHasMaps(++i, tables, to, row, data, hasMaps)
  },
  _getTables: function (subRule) {
    var tables = []
    var on = subRule.filter.on
    for (var tk in on) {
      if (tables.indexOf(tk) === -1) {
        tables.push(tk)
      }
      var t = on[tk]
      for (var p in t) {
        if (typeof t[p] === 'string' && t[p].indexOf('/') > -1) {
          var st = t[p].split('/')[0]
          if (tables.indexOf(st) === -1) {
            tables.push(st)
          }
        }
      }
    }
    return tables
  },
  /* 通过多条件过滤数 */
  _getDataFilterOn: function (subRule, to, row, data) {
    var tables = []
    tables = this._getTables(subRule, tables)
    var maps2 = this._loadHasMaps(0, tables, to, row, data, [])
    var fmaps = []
    maps2.forEach(function (item) {
      var on = subRule.filter.on
      for (var tk in on) {
        for (var p in on[tk]) {
          if (!item[tk]) {
            return
          }
          var pc = on[tk][p]
          if (typeof pc === 'string' && pc.indexOf('/') > -1) {
            var tp = this._getPath(pc)
            if (!item[tp.table]) {
              return
            }
            if (item[tk][p] !== item[tp.table][tp.field]) {
              return
            }
          } else if (item[tk][p] !== pc) {
            return
          }
        }
        fmaps.push(item)
      }
    })
    return fmaps
  },
  _loadPathValues: function (item, subRule, data, values, to, row) {
    var from = { table: item.split(this._split)[0], field: item.split(this._split)[1] }
    const ds = this._convertToArray(data[from.table])
    if (!subRule.filter || subRule.filter === 'all') {
      ds.forEach(function (r) {
        values.push(r[from.field])
      })
    }
    if (subRule.filter === 'f') {
      if (ds && ds.length > 0) {
        values.push(ds[0][from.field])
      }
    } else if (subRule.filter === 'cr' && from.table === to.table) {
      values.push(row[from.field])
    } else if (subRule.filter && subRule.filter.type === 'eq') {
      var filterto = subRule.filter.to
      ds.forEach(function (r) {
        if (util.typev(filterto) === 'array') {
          for (var i = 0; i < filterto.length; i++) {
            for (var key in filterto[i]) {
              if (r[filterto[i][key]] !== row[key]) {
                return
              }
            }
          }
        } else {
          for (const key in filterto) {
            if (r[filterto[key]] !== row[key]) {
              return
            }
          }
        }
        values.push(r[from.field])
      })
    } else if (subRule.filter && subRule.filter.type === 'on') {
      var fmaps = this._getDataFilterOn(subRule, to, row, data)
      fmaps.forEach(function (item) {
        values.push(item[from.table][from.field])
      })
    }
  },
  _getValues: function (data, row, rule, to, subRule, synName) {
    var dr = this
    var values = []
    var type = util.typev(subRule[synName])
    var rs = []
    if (type === 'array') {
      rs = subRule[synName]
    } else if (subRule[synName]) {
      rs.push(subRule[synName])
    }
    rs.forEach(function (item) {
      var t = util.typev(item)
      if (t === 'number') {
        values.push(item)
      } else if (t === 'string') {
        if (item.indexOf('/') === -1) {
          values.push(item)
          return
        }
        dr._loadPathValues(item, subRule, data, values, to, row)
      } else {
        var v = dr._getData(data, row, rule, to, item)
        if (util.typev(v) === 'array') {
          v.forEach(function (item) {
            values.push(item)
          })
        } else if (v) {
          values.push(v)
        }
      }
    })
    return values
  },
  _plus: function (data, row, rule, to, subRule) {
    var cr = this.calculates
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'plus')
    var result = 0
    values.forEach(function (v) {
      result = cr._plus(result, v)
    })
    result = cr.toFixed(result, subRule)
    return result
  },
  _minus: function (data, row, rule, to, subRule) {
    var cr = this.calculates
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'minus')
    var result = 0
    values.forEach(function (v) {
      result = cr._minus(result, v)
    })
    result = cr.toFixed(result, subRule)
    return result
  },
  _times: function (data, row, rule, to, subRule) {
    var cr = this.calculates
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'times')
    if (values.length < 2) {
      return 0
    }
    var result = values[0]
    for (var i = 1; i < values.length; i++) {
      result = cr._times(result, values[i])
    }
    result = cr.toFixed(result, subRule)
    return result
  },
  _divide: function (data, row, rule, to, subRule) {
    var cr = this.calculates
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'divide')
    var result = values[0]
    if (values.length > 1) {
      for (var i = 1; i < values.length; i++) {
        if (values[i]) {
          result = cr._divide(result, values[i])
        }
      }
    }
    result = cr.toFixed(result, subRule)
    return result
  },
  _count: function (data, row, rule, to, subRule) {
    var dr = this
    var values = []
    values = dr._getValues(data, row, rule, to, subRule, 'count')
    return values.length
  },
  _avg: function (data, row, rule, to, subRule) {
    var cr = this.calculates
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'avg')
    var result = 0
    values.forEach(function (r) {
      result = cr._plus(result, r)
    })
    result = cr._divide(result, values.length)
    result = cr.toFixed(result, subRule)
    return result
  },
  _joinSeparator: function (subRule, values) {
    var result = ''
    var separator = subRule.separator ? subRule.separator : ','
    for (var i = 0; i < values.length; i++) {
      if (values[i]) {
        if (!result) {
          result = values[i]
        } else {
          result += separator + values[i]
        }
      }
    }
    return result
  },
  _join: function (data, row, rule, to, subRule) {
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'join')
    var result = this._joinSeparator(subRule, values)
    return result
  },
  _list: function (data, row, rule, to, subRule) {
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'list')
    return values
  },
  _uniq: function (data, row, rule, to, subRule) {
    var dr = this
    var values = dr._getValues(data, row, rule, to, subRule, 'uniq')
    var distincts = []
    values.forEach(function (v) {
      if (distincts.indexOf(v) === -1) {
        distincts.push(v)
      }
    })
    return distincts
  },
  _sort: function (data, row, rule, to, subRule) {
    var dr = this
    var values = []
    values = dr._getValues(data, row, rule, to, subRule, 'sort')
    values = dr._chinesesort(values, subRule.order === 'desc')
    return values
  }, // 中文排序
  _chinesesort: function (values, desc) {
    var replaces = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    for (var i = 0; i < values.length; i++) {
      var nv = values[i]
      if (nv) {
        for (var j = 0; j < replaces.length; j++) {
          if (typeof nv === 'string') {
            nv = nv.replace(replaces[j], j)
          }
        }
      }
      values[i] = { v: values[i], nv: nv }
    }
    values.sort(function (l, n) {
      a = l.nv
      b = n.nv
      var c = isFinite(a) // 如果 number 是有限数字（或可转换为有限数字），那么返回 true。否则，如果 number 是 NaN（非数字），或者是正、负无穷大的数，则返回 false。
      var d = isFinite(b) // isFinite() 判断a、b是否是数字。
      return (c == d && a > b || c != d && a && !c) ? 1 : -1
    })
    for (var i = 0; i < values.length; i++) {
      values[i] = values[i].v
    }
    if (desc) {
      values = values.reverse()
    }
    return values
  },
  _distinct: function (data, row, rule, to, subRule) {
    // 去空+去重+排序的
    var dr = this
    var values = []
    values = dr._getValues(data, row, rule, to, subRule, 'distinct')
    var distincts = []
    values.forEach(function (v) {
      if (v && distincts.indexOf(v) === -1) {
        distincts.push(v)
      }
    })
    distincts = dr._chinesesort(distincts, subRule.order === 'desc')
    var result = this._joinSeparator(subRule, distincts)
    return result
  },
  _combine: function (data, row, rule, to, subRule) {
    var values = []
    values = this._getValues(data, row, rule, to, subRule, 'combine')
    var result = ''
    for (var i = 0; i < values.length; i++) {
      result += values[i]
    }
    return result
  },
  __addDays: function (date, days) {
    if (!date) return
    var nd = new Date(date)
    nd = nd.valueOf()
    nd = nd + days * 24 * 60 * 60 * 1000
    nd = new Date(nd)
    return nd
  },
  _rmb: function (data, row, rule, to, subRule) {
    const values = this._getValues(data, row, rule, to, subRule, 'rmb')
    for (var i = 0; i < values.length; i++) {
      if (values[i]) {
        return this._rmbChange(values[i], values[i + 1])
      }
    }
  },
  _addDay: function (data, row, rule, to, subRule) {
    var dr = this
    var value
    const values = this._getValues(data, row, rule, to, subRule, 'addDay')
    if (values.length === 0) {
      return value
    } else if (values.length === 1 && typeof subRule.num === 'number') {
      return dr.__addDays(values[0], subRule.num)
    }
    if (values.length === 2 && util.typev(values[0]) === 'date') {
      return dr.__addDays(values[0], Number(values[1]))
    }
    return value
  },
  triggerRule: function (rules, triggerType, data) {
    var dr = this
    rules.forEach(function (currentrule) {
      if (!currentrule.trigger) {
        return
      }
      var isTrigger = false
      var isEmpty = false
      currentrule.trigger.forEach(function (trigger) {
        if (trigger === triggerType) {
          isTrigger = true
        }
        if (trigger === 'isEmpty') {
          isEmpty = true
        }
      })
      if (isTrigger || isEmpty) {
        dr._runRule(currentrule, data, isEmpty)
        if (dr.enableLog) { console.log(currentrule) }
      }
    })
  },
  triggerNewRow: function (data, rules, tableName, row) {
    var cr = this.calculates
    var dr = this
    rules.forEach(function (currentrule) {
      if (!currentrule.to.startsWith(tableName + '/')) {
        return
      }
      if (dr.enableLog) console.log('runRule：', currentrule)
      var to = dr._getTo(currentrule)
      var result = dr._getData(data, row, currentrule, to, currentrule.from)
      result = cr.toFixed(result, currentrule)
      row[to.field] = result
    })
  },
  _rmbChange: function (money, type) {
    if (type) {
      money = (money * type).toFixed(0)
    }
    // 汉字的数字
    var cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
    // 基本单位
    var cnIntRadice = ['', '拾', '佰', '仟']
    // 对应整数部分扩展单位
    var cnIntUnits = ['', '万', '亿', '兆']
    // 对应小数部分单位
    var cnDecUnits = ['角', '分', '毫', '厘']
    // 整数金额时后面跟的字符
    var cnInteger = '整'
    // 整型完以后的单位
    var cnIntLast = '元'
    // 最大处理的数字
    var maxNum = 999999999999999.9999
    // 金额整数部分
    var integerNum
    // 金额小数部分
    var decimalNum
    // 输出的中文金额字符串
    var chineseStr = ''
    // 分离金额后用的数组，预定义
    var parts
    if (money === '') {
      return ''
    }
    money = parseFloat(money)
    if (money >= maxNum) {
      // 超出最大处理数字
      return ''
    }
    if (money === 0) {
      chineseStr = cnNums[0] + cnIntLast + cnInteger
      return chineseStr
    }
    // 转换为字符串
    money = money.toString()
    if (money.indexOf('.') === -1) {
      integerNum = money
      decimalNum = ''
    } else {
      parts = money.split('.')
      integerNum = parts[0]
      decimalNum = parts[1].substr(0, 4)
    }
    // 获取整型部分转换
    if (parseInt(integerNum, 10) > 0) {
      var zeroCount = 0
      var IntLen = integerNum.length
      for (var i = 0; i < IntLen; i++) {
        var n = integerNum.substr(i, 1)
        var p = IntLen - i - 1
        var q = p / 4
        var m = p % 4
        if (n == '0') {
          zeroCount++
        } else {
          if (zeroCount > 0) {
            chineseStr += cnNums[0]
          }
          // 归零
          zeroCount = 0
          chineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
        }
        if (m === 0 && zeroCount < 4) {
          chineseStr += cnIntUnits[q]
        }
      }
      chineseStr += cnIntLast
    }
    // 小数部分
    if (decimalNum != '') {
      var decLen = decimalNum.length
      for (var i = 0; i < decLen; i++) {
        var n = decimalNum.substr(i, 1)
        if (n != '0') {
          chineseStr += cnNums[Number(n)] + cnDecUnits[i]
        }
      }
    }
    if (chineseStr === '') {
      chineseStr += cnNums[0] + cnIntLast + cnInteger
    } else if (decimalNum === '') {
      chineseStr += cnInteger
    }
    return chineseStr
  },
  calculates: {
    _plus: function (num1, num2) { // 加
      var cr = this.calculates
      num1 = num1 || 0
      num2 = num2 || 0
      var num1Digits = (num1.toString().split('.')[1] || '').length
      var num2Digits = (num2.toString().split('.')[1] || '').length
      var baseNum = Math.pow(10, Math.max(num1Digits, num2Digits))
      return (cr._times(num1, baseNum) + cr._times(num2, baseNum)) / baseNum
    },
    _minus: function (num1, num2) { // 减
      var cr = this.calculates
      num1 = num1 || 0
      num2 = num2 || 0
      var num1Digits = (num1.toString().split('.')[1] || '').length
      var num2Digits = (num2.toString().split('.')[1] || '').length
      var baseNum = Math.pow(10, Math.max(num1Digits, num2Digits))
      return (cr._times(num1, baseNum) - cr._times(num2, baseNum)) / baseNum
    },
    _times: function (num1, num2) { // 乘
      num1 = num1 || 0
      num2 = num2 || 0
      var num1String = num1.toString()
      var num2String = num2.toString()
      var num1Digits = (num1String.split('.')[1] || '').length
      var num2Digits = (num2String.split('.')[1] || '').length
      var baseNum = Math.pow(10, num1Digits + num2Digits)
      return Number(num1String.replace('.', '')) * Number(num2String.replace('.', '')) / baseNum
    },
    _divide: function (num1, num2) { // 除
      var cr = this.calculates
      num1 = num1 || 0
      num2 = num2 || 0
      var num1String = num1.toString()
      var num2String = num2.toString()
      var num1Digits = (num1String.split('.')[1] || '').length
      var num2Digits = (num2String.split('.')[1] || '').length
      var baseNum = Math.pow(10, num1Digits + num2Digits)
      var n1 = cr._times(num1, baseNum)
      var n2 = cr._times(num2, baseNum)
      return Number(n1) / Number(n2)
    },
    _floatRound: function (num, len) { // len表示保留几位数小数
      len = len || 2
      var cr = this.calculates
      var n = cr._divide(Math.round(cr._times(num, Math.pow(10, len))), Math.pow(10, len))
      return n.toFixed(len)
    },
    toFixed: function (value, subRule) {
      var cr = this.calculates
      if (subRule && subRule.toFixed) {
        if (value === 0) {
          return 0
        }
        return cr._floatRound(value, subRule.toFixed)
      }
      return value
    }
  }

}

const util = {}

// 类型判断
const ctype = util || {};
(function (obj) {
  function typev (obj) {
    return Object.prototype.toString.call(obj).split(/^\[object ([a-zA-Z]*)\]/)[1].toLowerCase()
  }

  function isArray (obj) {
    return typev(obj) === 'array'
  }

  function isDate (obj) {
    return typev(obj) === 'date'
  }

  function isNumber (obj) {
    return typev(obj) === 'number' && !isNaN(obj)
  }

  function isString (obj) {
    return typev(obj) === 'string'
  }

  function isBoolean (obj) {
    return typev(obj) === 'boolean'
  }

  function isObject (obj) {
    return typev(obj) === 'object'
  }

  function isNull (obj) {
    return typev(obj) === 'null'
  }

  function isUndefined (obj) {
    return typev(obj) === 'undefined'
  }

  function isMouseEvent (obj) {
    return typev(obj) === 'mouseevent'
  }

  function isFunction (obj) {
    return typev(obj) === 'function'
  }

  function isLocation (obj) {
    return typev(obj) === 'location'
  }

  var getProto = Object.getPrototypeOf
  var class2type = {}
  var toString = class2type.toString
  var hasOwn = class2type.hasOwnProperty
  var fnToString = hasOwn.toString
  var ObjectFunctionString = fnToString.call(Object)

  function isPlainObject (obj) {
    var proto, Ctor
    if (!obj || toString.call(obj) !== '[object Object]') {
      return false
    }
    proto = getProto(obj)
    if (!proto) return true

    Ctor = hasOwn.call(proto, 'constructor') && proto.constructor
    return typeof Ctor === 'function' && fnToString.call(Ctor) === ObjectFunctionString
  }

  obj.typev = typev
  obj.isArray = isArray
  obj.isDate = isDate
  obj.isNumber = isNumber
  obj.isString = isString
  obj.isBoolean = isBoolean
  obj.isObject = isObject
  obj.isNull = isNull
  obj.isUndefined = isUndefined
  obj.isMouseEvent = isMouseEvent
  obj.isFunction = isFunction
  obj.isPlainObject = isPlainObject
  obj.isLocation = isLocation
})(ctype)
export { DataSyn }
